`include "PRV564Config.v"
`include "PRV564Define.v"
//////////////////////////////////////////////////////////////////////////////////////////////////
//  Date    : 2021                                                                              //
//  Author  : Jack.Pan                                                                          //
//  Desc    : Address Translate Unit for PRV564 processor, which include a TLB and PTW          //
//  Version : 3.0(Version 3)                                                                    //
//////////////////////////////////////////////////////////////////////////////////////////////////
module ATU
#(
    parameter FIB_ID        = 8'h01,
    parameter TLB_entry_NUM = 16,
    parameter InstrATU      = 0                     //default as NOT an InstrFront
)
(
//Global Signals
    input wire              ATUi_CLK,
    input wire              ATUi_ARST,
    input wire              ATUi_Flush,             //Request FLush pipline
    input wire              ATUi_ModifyPermit,      //允许模块进行内容修改
    input wire [7:0]        ATUi_ModifyPermitID,    //允许修改的指令ID，如果当前ID等于此ID，则开始修改
    input wire              ATUi_TLBrefersh,        //(From Master) Request refersh TLB
    input wire              ATUi_CacheRefersh,
    input wire              ATUi_CSR_CacheInhibit,
    input wire              ATUi_CSR_CacheWT,
    input wire              ATUi_CSR_mxr,
    input wire              ATUi_CSR_sum,
    input wire [3:0]        ATUi_CSR_satpmode,      //satp.mode
    input wire [43:0]       ATUi_CSR_satpppn,       //satp.ppn
//Pipline input signals
    input  wire             PIP_ATUi_MSC_valid,     //操作有效
    input  wire  [7:0]      PIP_ATUi_Opcode,        //8bit opcode
    input  wire  [1:0]      PIP_ATUi_OpInfo,        //Operation Information, include Unsign/Sign and RV32/64
    input  wire  [3:0]      PIP_ATUi_OpSize,
    input  wire  [7:0]      PIP_ATUi_INFO_ITAG,
    input  wire  [1:0]      PIP_ATUi_INFO_priv,     //权限
    input  wire             PIP_ATUi_INFO_unpage,   //unpage mode enable
    input  wire [`XLEN-1:0] PIP_ATUi_INFO_PC,       //Instruction Infomation: PC value
    input  wire [`XLEN-1:0] PIP_ATUi_DATA_VA,       //虚拟地址输入
    input  wire [`XLEN-1:0] PIP_ATUi_DATA_ds2,
    output reg              PIP_ATUo_FC_ready,
//------------------write to access table-----------------
    output reg              Tablei_WREN,
    output     [7:0]        Tablei_WID,
    output     [`XLEN-1:0]  Tablei_ADDR,
    output     [`XLEN-1:0]  Tablei_PC,
    output     [7:0]        Tablei_ITAG,
    output     [1:0]        Tablei_priv,
    output     [7:0]        Tablei_opcode,
    output     [1:0]        Tablei_opinfo,
    output     [3:0]        Tablei_opsize,
    output                  Tablei_ci,
    output                  Tablei_InstPageFlt,
    output reg              Tablei_InstAddrMis,
    output                  Tablei_LoadPageFlt,
    output reg              Tablei_LoadAddrMis,
    output                  Tablei_StorePageFlt,
    output reg              Tablei_StoreAddreMis,
    input wire              Tableo_FULL,
//------------------issue to access queue-----------------
    output reg              ATUo_AQ_V,          //add a new access to Access Queue
    output     [7:0]        ATUo_AQ_ID,         //new access's ID
    output reg [7:0]        ATUo_AQ_CMD,        //command and
    output reg              ATUo_AQ_CI,         //cache is inhibit
    output reg              ATUo_AQ_WT,         //write through is needed
    output wire [15:0]      ATUo_AQ_BSEL,       //Byte select
    output wire [127:0]     ATUo_AQ_WDATA,      //write data (or exchange data)
    output reg [`XLEN-1:0]  ATUo_AQ_ADDR,
    input wire              ATUi_AQ_FULL,
//------------FIB bus interface--------------
    output wire             ATUo_FIB_WREN,        //write to FIB enable
    output wire             ATUo_FIB_REQ,         //request FIB trans
    input  wire             ATUi_FIB_ACK,         //request acknowledge
    input  wire             ATUi_FIB_FULL,        //FIB FIFO full
    output wire [7:0]       ATUo_FIB_ID,
    output wire [7:0]       ATUo_FIB_CMD,
    output wire [3:0]       ATUo_FIB_BURST,
    output wire [3:0]       ATUo_FIB_SIZE,
    output wire [`XLEN-1:0] ATUo_FIB_ADDR,      
    output wire [`XLEN-1:0] ATUo_FIB_DATA,
    input  wire [7:0]       ATUi_FIB_ID,
    input  wire [7:0]       ATUi_FIB_RPL,
    input  wire             ATUi_FIB_V,
    input  wire [`XLEN-1:0] ATUi_FIB_DATA

);
    wire                Valid;                  //Global valid for whole module
    wire [7:0]          AccessID;               //access ID
    reg                 ID_GEN;                 //generate a new ID
    reg                 FLAG_CacheRef;          //A cache refersh is pending
	 reg                 FLAG_Clean;
//--------------------------对齐检查---------------------------------
    wire                MisAligned;
//--------------------------TLB output-------------------------------
    wire                TLBo_valid;
    wire TLBo_DATA_WrThrough,TLBo_DATA_Cacheable;
    wire [`XLEN-1:0]    TLBo_DATA_PA;
    wire                TLBo_InstPageFlt, TLBo_LoadPageFlt, TLBo_StorePageFlt;
//-------------------------PTW command and reply-----------------------
    wire [7:0]          PTWi_CMD;           //command 
    wire                PTWi_V;             //Valid
    wire [3:0]          PTWi_WalkID;         //Current translation ID
    wire [26:0]         PTWi_VPN;           //Virtual Address Input
    wire                PTWo_FULL;          //PTW is full!
    wire [43:0]         PTWo_PPN;           //Physical Page Number Output(After Translation)
    wire [7:0]          PTWo_RPL;
    wire [3:0]          PTWo_WalkID;
    wire                PTWo_V;
    wire [9:0]          PTWo_PTE;           //Page Table Entry
    wire [1:0]          PTWo_PageSize;      //Page size 2:1G, 1:2M, 0:4K
    wire                PTWi_RDEN;          //read a result

assign Valid = !ATUi_Flush & PIP_ATUi_MSC_valid;
//----------------------------Cache refersh flag----------------------------
always@(posedge ATUi_CLK or posedge ATUi_ARST)begin
    if(ATUi_ARST)begin
        FLAG_CacheRef <= 1'b0;
    end
    else if(ATUi_CacheRefersh)begin
        FLAG_CacheRef <= 1'b1;
    end
    else if(FLAG_Clean)begin
        FLAG_CacheRef <= 1'b0;
    end
end
//--------------------------字节选择信号和地址对齐检查-----------------
DataShiftL      DataShiftL(
    .Offset_ADDR            (PIP_ATUi_DATA_VA[3:0]),
    .DATAi                  (PIP_ATUi_DATA_ds2),
    .SIZEi                  (PIP_ATUi_OpSize),
    .MisAligned             (MisAligned),         //当前访问不对齐
    .BSELo                  (ATUo_AQ_BSEL),
    .DATAo                  (ATUo_AQ_WDATA)
);
//-------------------------TLB access signal--------------------------
    reg             TLBi_valid;
always@(*)begin
    if(Valid)begin
        if(MisAligned)begin             //如果有地址不对齐，则停止访问
            TLBi_valid = 1'b0;
        end
        else if((ATUi_CSR_satpmode==`Sv39_On) & !PIP_ATUi_INFO_unpage)begin
            TLBi_valid = 1'b1;
        end
        else begin
            TLBi_valid = 1'b0;
        end
    end
    else begin
        TLBi_valid = 1'b0;
    end
end
//--------------------------TLB---------------------------------------
TLB#(.TLB_entry_NUM (TLB_entry_NUM))TLB(
    //Global Signals
    .TLBi_CLK               (ATUi_CLK),
    .TLBi_ARST              (ATUi_ARST),
    .TLBi_ModifyPermit      (ATUi_ModifyPermit & (ATUi_ModifyPermitID==PIP_ATUi_INFO_ITAG)),
    .TLBi_pipflush          (ATUi_Flush),
    .TLBi_refersh           (ATUi_TLBrefersh),           //(From Master) Request refersh TLB
    .TLBi_privilege         (PIP_ATUi_INFO_priv),
    .TLBi_CSR_mxr           (ATUi_CSR_mxr),
    .TLBi_CSR_sum           (ATUi_CSR_sum),
//---------------------TLB access signals---------------------
    .TLBi_valid             (TLBi_valid),         //操作有效
    .TLBi_Opcode            (PIP_ATUi_Opcode),        //8bit opcode
    .TLBi_DATA_VA           (PIP_ATUi_DATA_VA),       //虚拟地址输入
//---------------------TLB output----------------------------
    .TLBo_valid             (TLBo_valid),
    .TLBo_LoadPageFlt       (TLBo_LoadPageFlt),
    .TLBo_StorePageFlt      (TLBo_StorePageFlt),
    .TLBo_InstPageFlt       (TLBo_InstPageFlt),
    .TLBo_DATA_PA           (TLBo_DATA_PA),        //物理地址输出
    .TLBo_DATA_Cacheable    (TLBo_DATA_Cacheable), //可以被缓存
    .TLBo_DATA_WrThrough    (TLBo_DATA_WrThrough), //此地址需要被写穿透
//----------------------Connect to PTW----------------------
    .PTWi_CMD               (PTWi_CMD),           //command 
    .PTWi_V                 (PTWi_V),             //Valid
    .PTWi_WalkID            (PTWi_WalkID),         //Current translation ID
    .PTWi_VPN               (PTWi_VPN),           //Virtual Address Input
    .PTWo_FULL              (PTWo_FULL),          //PTW is full!
//---------PTE and PPN Reply-----------------
    .PTWo_PPN               (PTWo_PPN),           //Physical Page Number Output(After Translation)
    .PTWo_RPL               (PTWo_RPL),
    .PTWo_WalkID            (PTWo_WalkID),
    .PTWo_V                 (PTWo_V),
    .PTWo_PTE               (PTWo_PTE),           //Page Table Entry
    .PTWo_PageSize          (PTWo_PageSize),      //Page size 2:1G, 1:2M, 0:4K
    .PTWi_RDEN              (PTWi_RDEN)           //read a result
);
//-----------------------------Page Table Walker----------------------------------
PTW#(
    .FIB_ID(FIB_ID)
) PTW(
    .PTWi_CLK               (ATUi_CLK),
    .PTWi_ARST              (ATUi_ARST),
    .PTWi_CSR_privlage      (PIP_ATUi_INFO_priv),
    .PTWi_CSR_sum           (ATUi_CSR_sum),
    .PTWi_CSR_mxr           (ATUi_CSR_mxr),
    .PTWi_CSR_satpppn       (ATUi_CSR_satpppn),
    .PTWi_CMD               (PTWi_CMD),
    .PTWi_V                 (PTWi_V),
    .PTWi_WalkID            (PTWi_WalkID),
    .PTWi_VPN               (PTWi_VPN),
    .PTWo_FULL              (PTWo_FULL),
//---------PTE and PPN Reply-----------------
    .PTWo_PPN               (PTWo_PPN),
    .PTWo_RPL               (PTWo_RPL),
    .PTWo_WalkID            (PTWo_WalkID),
    .PTWo_V                 (PTWo_V),
    .PTWo_PTE               (PTWo_PTE),
    .PTWo_PageSize          (PTWo_PageSize),
    .PTWi_RDEN              (PTWi_RDEN),
//------------PTW FIB bus--------------------
    .PTWo_FIB_WREN          (ATUo_FIB_WREN),
    .PTWo_FIB_REQ           (ATUo_FIB_REQ),
    .PTWi_FIB_ACK           (ATUi_FIB_ACK),
    .PTWi_FIB_FULL          (ATUi_FIB_FULL),
    .PTWo_FIB_ID            (ATUo_FIB_ID),
    .PTWo_FIB_CMD           (ATUo_FIB_CMD),
    .PTWo_FIB_BURST         (ATUo_FIB_BURST),
    .PTWo_FIB_SIZE          (ATUo_FIB_SIZE),
    .PTWo_FIB_ADDR          (ATUo_FIB_ADDR),
    .PTWo_FIB_DATA          (ATUo_FIB_DATA),
    .PTWi_FIB_ID            (ATUi_FIB_ID),
    .PTWi_FIB_RPL           (ATUi_FIB_RPL),
    .PTWi_FIB_V             (ATUi_FIB_V),
    .PTWi_FIB_DATA          (ATUi_FIB_DATA)
);
//-------------------------------Generate Access ID------------------------------
TAGgen      TAGgen(
    .CLKi                   (ATUi_CLK),           //clock input
    .ARSTi                  (ATUi_ARST),          //Async reset input
    .ENi                    (ID_GEN),            //随机数产生使能，为1时在下一个cycle生成新的随机数，为0时保持
    .DATAo                  (AccessID)           //随机数输出
);
//--------------------------Write to access table and issue-----------------------
always@(*)begin
    if(Valid)begin
        if(Tableo_FULL | ATUi_AQ_FULL)begin                 //Access Queue和Table全满，不写表项
            Tablei_WREN         = 1'b0;
            ATUo_AQ_V           = 1'b0;
            ATUo_AQ_CMD         = 'hx; 
            ID_GEN              = 1'b0;
            PIP_ATUo_FC_ready   = 1'b0;
            FLAG_Clean          = 1'b0;
        end
        else if(FLAG_CacheRef)begin             //当前有一个冲刷正在等待，将冲刷发射到Cache中
            Tablei_WREN         = 1'b0;
            ATUo_AQ_V           = 1'b1;
            ATUo_AQ_CMD         = `LSU_CacheRef; 
            ID_GEN              = 1'b1;
            PIP_ATUo_FC_ready   = 1'b0;
            FLAG_Clean          = 1'b1;
        end
        else if(PIP_ATUi_Opcode==`LSU_CacheRef)begin
            Tablei_WREN         = 1'b1;
            ATUo_AQ_V           = 1'b1;
            ATUo_AQ_CMD         = `LSU_CacheRef;        
            ID_GEN              = 1'b1;
            PIP_ATUo_FC_ready   = 1'b1;
            FLAG_Clean          = 1'b0;
        end
        else if(MisAligned)begin
            Tablei_WREN         = 1'b1;
            ATUo_AQ_V           = 1'b1;
            ATUo_AQ_CMD         = `LSU_NOP;             
            ID_GEN              = 1'b1;
            PIP_ATUo_FC_ready   = 1'b1;
            FLAG_Clean          = 1'b0;
        end
        else if((ATUi_CSR_satpmode==`Sv39_On) & !PIP_ATUi_INFO_unpage)begin                //当前打开分页模式
            if(TLBo_valid)begin
               if(TLBo_InstPageFlt | TLBo_LoadPageFlt | TLBo_StorePageFlt)begin //如果有页面错误，直接发送NOP操作到队列中
                    Tablei_WREN         = 1'b1;
                    ATUo_AQ_V           = 1'b1;
                    ATUo_AQ_CMD         = `LSU_NOP;             
                    ID_GEN              = 1'b1;
                    PIP_ATUo_FC_ready   = 1'b1;
                    FLAG_Clean          = 1'b0;
               end
               else if((PIP_ATUi_Opcode==`LSU_READ)|(PIP_ATUi_Opcode==`LSU_READ_Lock)|(PIP_ATUi_Opcode==`LSU_eXecute))begin //若是读内存的操作，不需要等到modifypermit
                    Tablei_WREN         = 1'b1;
                    ATUo_AQ_V           = 1'b1;
                    ATUo_AQ_CMD         = `LSU_READ;
                    ID_GEN              = 1'b1;
                    PIP_ATUo_FC_ready   = 1'b1;
                    FLAG_Clean          = 1'b0;
               end
               else if(ATUi_ModifyPermit & (ATUi_ModifyPermitID==PIP_ATUi_INFO_ITAG))begin
                    Tablei_WREN         = 1'b1;
                    ATUo_AQ_V           = 1'b1;
                    ATUo_AQ_CMD         = PIP_ATUi_Opcode;
                    ID_GEN              = 1'b1;
                    PIP_ATUo_FC_ready   = 1'b1;
                    FLAG_Clean          = 1'b0;
               end
               else begin
                    Tablei_WREN         = 1'b0;
                    ATUo_AQ_V           = 1'b0;
                    ATUo_AQ_CMD         = 'hx;           
                    ID_GEN              = 1'b0;
                    PIP_ATUo_FC_ready   = 1'b0;
                    FLAG_Clean          = 1'b0;
               end
            end
            else begin          //当前TLB尚未准备好
                Tablei_WREN         = 1'b0;
                ATUo_AQ_V           = 1'b0;
                ATUo_AQ_CMD         = 'hx;
                ID_GEN              = 1'b0;
                PIP_ATUo_FC_ready   = 1'b0;
                FLAG_Clean          = 1'b0;
            end
        end
        else if((PIP_ATUi_Opcode==`LSU_READ)|(PIP_ATUi_Opcode==`LSU_READ_Lock)|(PIP_ATUi_Opcode==`LSU_eXecute))begin //当前虚拟内存没有打开
            Tablei_WREN         = 1'b1;
            ATUo_AQ_V           = 1'b1;
            ATUo_AQ_CMD         = `LSU_READ;
            ID_GEN              = 1'b1;
            PIP_ATUo_FC_ready   = 1'b1;
            FLAG_Clean          = 1'b0;
        end
        else if(ATUi_ModifyPermit & (ATUi_ModifyPermitID==PIP_ATUi_INFO_ITAG))begin  //虚拟化内存没有打开，直接传
            Tablei_WREN         = 1'b1;
            ATUo_AQ_V           = 1'b1;
            ATUo_AQ_CMD         = PIP_ATUi_Opcode;             
            ID_GEN              = 1'b1;
            PIP_ATUo_FC_ready   = 1'b1;
            FLAG_Clean          = 1'b0;
        end
        else begin
            Tablei_WREN         = 1'b0;
            ATUo_AQ_V           = 1'b0;
            ATUo_AQ_CMD         = 'hx;           
            ID_GEN              = 1'b0;
            PIP_ATUo_FC_ready   = 1'b0;
            FLAG_Clean          = 1'b0;
        end
    end
    else begin
        Tablei_WREN         = 1'b0;
        ATUo_AQ_V           = 1'b0;
        ATUo_AQ_CMD         = 'hx; 
        ID_GEN              = 1'b0;
        PIP_ATUo_FC_ready   = ATUi_Flush ? 1'b1 : 1'b0;
        FLAG_Clean          = 1'b0;
    end
end
//-----------------------AQ---------------------------
assign ATUo_AQ_ID      = AccessID;
always@(*)begin
    if((ATUi_CSR_satpmode==`Sv39_On) & !PIP_ATUi_INFO_unpage)begin     //virtual address mode on!

        ATUo_AQ_WT      = TLBo_DATA_WrThrough | ATUi_CSR_CacheWT;
        ATUo_AQ_ADDR    = TLBo_DATA_PA;
        ATUo_AQ_CI      = !(TLBo_DATA_Cacheable | (((ATUo_AQ_ADDR & `Cacheable_MASK) == `Cacheable_ADDR))) | ATUi_CSR_CacheInhibit;
    end
    else begin
        //ATUo_AQ_ID      = AccessID;
        ATUo_AQ_WT      = ATUi_CSR_CacheWT;
        ATUo_AQ_ADDR    = PIP_ATUi_DATA_VA;
        ATUo_AQ_CI      = !(((ATUo_AQ_ADDR & `Cacheable_MASK) == `Cacheable_ADDR)) | ATUi_CSR_CacheInhibit;
    end
end
//---------------------------to table------------------------------
//-------------MisAligned------
always@(*)begin
    if(Valid & MisAligned)begin
        if(PIP_ATUi_Opcode==`LSU_eXecute)begin
            Tablei_InstAddrMis = 1'b1;
            Tablei_LoadAddrMis = 1'b0;
            Tablei_StoreAddreMis= 1'b0;
        end
        else if((PIP_ATUi_Opcode==`LSU_READ) | (PIP_ATUi_Opcode==`LSU_READ_Lock))begin
            Tablei_InstAddrMis = 1'b0;
            Tablei_LoadAddrMis = 1'b1;
            Tablei_StoreAddreMis= 1'b0;
        end
        else begin
            Tablei_InstAddrMis = 1'b0;
            Tablei_LoadAddrMis = 1'b0;
            Tablei_StoreAddreMis= 1'b1;
        end
    end
    else begin
        Tablei_InstAddrMis = 1'b0;
        Tablei_LoadAddrMis = 1'b0;
        Tablei_StoreAddreMis= 1'b0;
    end
end
//always@(*)begin
assign Tablei_WID          = AccessID;
assign Tablei_ADDR         = InstrATU ?PIP_ATUi_DATA_ds2 : PIP_ATUi_DATA_VA;   //如果是工作在指令前端模式下，ADDR记录预测值；如果工作在LSU模式下，ADDR记录当前指令访问的虚拟地址。
assign Tablei_PC           = PIP_ATUi_INFO_PC;
assign Tablei_ITAG         = PIP_ATUi_INFO_ITAG;
assign Tablei_priv         = PIP_ATUi_INFO_priv;
assign Tablei_opcode       = PIP_ATUi_Opcode;
assign Tablei_opinfo       = PIP_ATUi_OpInfo;
assign Tablei_opsize       = PIP_ATUi_OpSize;
assign Tablei_ci           = ATUo_AQ_CI;
assign Tablei_InstPageFlt  = TLBo_InstPageFlt;
assign Tablei_LoadPageFlt  = TLBo_LoadPageFlt;
assign Tablei_StorePageFlt = TLBo_StorePageFlt;
//end


endmodule

//         64bit to 128bit data shift
module DataShiftL(
    input wire [3:0]    Offset_ADDR,
    input wire [63:0]   DATAi,
    input wire [3:0]    SIZEi,
    output reg          MisAligned,         //当前访问不对齐
    output wire [15:0]  BSELo,
    output wire [127:0] DATAo
);
    wire [127:0] shift0, shift1, shift2;
    wire [15:0]  bsel_base, bsel0, bsel1, bsel2;
//---------------------生成移位后的数据----------------------
assign shift0 = Offset_ADDR[0] ? {56'b0,DATAi,8'b0} : {64'b0, DATAi};
assign shift1 = Offset_ADDR[1] ? {shift0[111:0],16'b0} : shift0;
assign shift2 = Offset_ADDR[2] ? {shift1[95:0],32'b0} : shift1;
assign DATAo  = Offset_ADDR[3] ? {shift2[63:0],64'b0} : shift2;
//------------------生成字节掩码-----------------------------
assign bsel_base =  (SIZEi[0] ? 16'b00000000_00000001 : 16'b00000000_00000000) |
                    (SIZEi[1] ? 16'b00000000_00000011 : 16'b00000000_00000000) |
                    (SIZEi[2] ? 16'b00000000_00001111 : 16'b00000000_00000000) |
                    (SIZEi[3] ? 16'b00000000_11111111 : 16'b00000000_00000000);     //
assign bsel0 = Offset_ADDR[0] ? {bsel_base[14:0],1'b0} : bsel_base;
assign bsel1 = Offset_ADDR[1] ? {bsel0[13:0],2'b0} : bsel0;
assign bsel2 = Offset_ADDR[2] ? {bsel1[11:0],4'b0} : bsel1;
assign BSELo = Offset_ADDR[3] ? {bsel2[7:0],8'b0} : bsel2;
//产生不对齐信号
always@(*)begin
    case(SIZEi)
        4'h1 : MisAligned = 1'b0; 
        4'h2 : MisAligned = (Offset_ADDR[0] != 1'b0);
        4'h4 : MisAligned = (Offset_ADDR[1:0] != 2'b00);
        4'h8 : MisAligned = (Offset_ADDR[2:0] != 3'b000);
    default  : MisAligned = 1'b0;
    endcase
end

endmodule
